/*
 * @file iso9660_record.h
 *
 *  ISO9660 record management
 *
 */

#ifndef __ISO9660_RECORD_H_
#define __ISO9660_RECORD_H_

#include "iso9660.h"


typedef struct iso9660_record_cache_s {
  iso9660_sector_t        sector;         /**< Sector number */
  uint32_t                count;          /**< Sector count */
  char                   *buffer;         /**< Buffer content */
  uint8_t                 use;            /**< Use counter */
  uint32_t                offset;         /**< Offset of the buffer in the file section */
} iso9660_record_cache_t;


typedef struct iso9660_record_s {
  uint8_t                 record_len;     /**< Length of the record */
  uint32_t                size;           /**< Data size */
  iso9660_sector_t        extent;         /**< Extent containing this record */
  uint16_t                offset;         /**< Offset in the extent */
  iso9660_sector_t        location;       /**< Record content location */

  char                   *name;           /**< Record file/directory name */
  uint8_t                 name_len;       /**< Length of the above field */

  struct timespec        date;           /**< Recording date & time */

  iso9660_dr_flags_t      flags;          /**< Record flags */

  void                   *system_use;     /**< System use area */
  uint16_t                system_use_size;/**< Size of the system use area */
  iso9660_fs_info_t      *fs;             /**< Associated filesystem informations */
  iso9660_record_cache_t *cache;          /**< Cache for directory operations */
} iso9660_record_t;

#define ISO9660_RECORD_SKIP_CURRENT   (1<<0)
#define ISO9660_RECORD_SKIP_PARENT    (1<<1)


/**
 *   Convert a ISO9660 directory/file record date to a standard C timespec.
 * The nsec member of the timespec structure will always be initialized to
 * zero.
 *
 * @param d Pointer to the iso9660_date_t date member of the record.
 *
 * @return A timespec structure equal describing the same time value.
 */
struct timespec
iso9660_record_date_to_timespec(
    iso9660_date_t  *d
);


iso9660_record_t*
iso9660_record_create_from_extent(
    iso9660_extent_t    extent,
    iso9660_fs_info_t   *fs
);

/**
 *   Create an ISO9660 record to use among the various record
 * management functions from a standard ISO9660 directory record.
 *
 * TODO: Use a circular list to cache the created records.
 *
 * @param dir_record  Pointer to a standard iso9660 directory record
 * @param fs          Filesystem information structure pointer
 *
 * @return Either NULL if the creation failed or a pointer to the created
 *         record descriptor.
 */
iso9660_record_t*
iso9660_record_create(
    iso9660_directory_record_t *dir_record,
    iso9660_sector_t            extent,
    iso9660_fs_info_t          *fs
);

/**
 *    Free the memory allocated by iso9660_record_create. If the record
 * hasn't been closed and the cache is still held the call will fail.
 *
 * @param record A pointer to the record descriptor which should be freed
 *
 * @return 0 if sucessful, else -1
 */
int
iso9660_record_free(
    iso9660_record_t    *record
);

/**
 *   Make a deep copy of the record pointed out by source in
 * the record pointed out by dest. If the cache was opened in
 * source, its use counter is incremented.
 *
 * @param dest   Destination record
 * @param source Source record
 *
 * @return 0 is the copy is OK, else -1
 */
int
iso9660_record_copy(
    iso9660_record_t  *dest,
    iso9660_record_t  *source
);

/**
 *     Open a record descriptor to allow directory record traversal or read
 *  operations on a file. This function loads the corresponding sector in memory
 *  to avoid further disk access when accessing the list of records in case of a
 *  directory.
 *
 *  TODO: Use a global cache for all sectors
 *
 *  @param record  A pointer to the record descriptor to "open"
 *
 *  @return 0 if opening is successful, else -1
 */
int iso9660_record_open(
    iso9660_record_t    *record
);

/**
 *   Fetch the next entry in a directory record list.
 *
 * @param record  A pointer to the record descriptor
 * @param offset  Offset in the record list returned by a previous
 *                call to this function
 * @param entry   Fetched entry in the directory (NULL if there is no more entries)
 * @param flags   A set of flags to tweak the function :
 *                   - ISO9660_RECORD_SKIP_CURRENT : Skip the "current" (.) entry
 *                   - ISO9660_RECORD_SKIP_PARENT : Skip the "parent" (..) entry
 *
 * @return 0 if there is no more entries in the directory record,
 *         -1 if an error occured,
 *         an offset for the next entry if everything is ok
 */
int iso9660_record_next_entry(
    iso9660_record_t    *record,
    uint16_t             offset,
    iso9660_record_t    *entry,
    uint8_t              flags
);

/**
 *    Ensure that the record descriptor isn't used by anyone else and
 *  free the previously allocated cache.
 *
 *  @param record  A pointer to the record descriptor to "close"
 *
 *  @return 0 if the record is correctly closed, else -1
 */
int iso9660_record_close(
    iso9660_record_t    *record
);


int iso9660_record_stat(
    iso9660_record_t    *record,
    struct stat        *buf,
    char                dir
);

#define iso9660_record_is_parent(_rec) \
          (((_rec)->name_len == 2) && ((_rec)->name[0] == '.') && ((_rec)->name[1] == '.'))

#define iso9660_record_is_current(_rec) \
          (((_rec)->name_len == 1) && ((_rec)->name[0] == '.'))

#endif /* __ISO9660_RECORD_H_ */
